package com.haier.crypt;


import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

/**
 * 加解密工具，以下为示例代码，采用 java 实现作为示意，实际开发中，
 * 该部分使用 C/C++ 进行开发，打包成 so 库对外提供
 *
 * 请勿直接使用示例代码进行加解密！！！！！
 */

public class CryptUtils {

    private static final String TAG = CryptUtils.class.getSimpleName();
    private static final String KEY_ALGORITHM = "AES";  // 加密算法
    public static final int SECRET_KEY_LENGTH = 32; // AES 的密钥长度，32 字节，范围：16 - 32 字节
    private static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8; // 字符编码
    private static final String DEFAULT_VALUE = "0"; // 秘钥长度不足 16 个字节时，默认填充位数
    private static final String FILE_CIPHER_ALGORITHM = "AES/CBC/PKCS5Padding";
    public static final String secretKey = "qweasd123"; // 加密密码，长度：16 或 32 个字符(随便定义)

    /**
     * 使用密码获取 AES 秘钥
     */
    public static SecretKeySpec getSecretKey(String secretKey) {
        secretKey = toStandardKey(secretKey, SECRET_KEY_LENGTH, DEFAULT_VALUE);
        return new SecretKeySpec(secretKey.getBytes(CHARSET_UTF8), KEY_ALGORITHM);
    }

    /**
     * 如果 AES 的密钥不足，对秘钥进行补位，保证秘钥安全。
     *
     * @param secretKey 密钥 key
     * @param length 密钥应有的长度
     * @param text 默认补的文本
     * @return 密钥
     */
    private static String toStandardKey(String secretKey, int length, String text) {
        int strLen = secretKey.length();
        if (strLen < length) {
            StringBuilder builder = new StringBuilder(); // 补全位数
            builder.append(secretKey);
            for (int i = 0; i < length - strLen; i++) {
                builder.append(text);
            }
            secretKey = builder.toString();
        }
        return secretKey;
    }

    /**
     * 对文件进行 AES 加密
     *
     * @param sourceFile 待加密文件
     * @param dir 加密后的文件存储路径
     * @param toFileName 加密后的文件名称
     * @param secretKey 密钥
     * @return 加密后的文件
     */
    public static File encryptFile(File sourceFile, String dir, String toFileName, String secretKey) {
        File encryptFile = new File(dir, toFileName);   // 创建加密后的文件
        Cipher cipher = initFileAESCipher(secretKey, Cipher.ENCRYPT_MODE);
        System.out.println("encryptFile sourceFile " + sourceFile.getAbsolutePath());
        System.out.println("encryptFile dir " + dir);
        System.out.println("encryptFile toFileName " + toFileName);
        System.out.println("encryptFile secretKey " + secretKey);
        try (FileOutputStream outputStream = new FileOutputStream(encryptFile);
             CipherInputStream cipherInputStream = new CipherInputStream(new FileInputStream(sourceFile), cipher)) {

            byte[] buffer = new byte[1024 * 2];
            int len;
            while ((len = cipherInputStream.read(buffer)) > 0) {
                outputStream.write(buffer, 0, len);
                outputStream.flush();
            }
            return encryptFile;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * AES解密文件
     *
     * @param sourceFile 源加密文件
     * @param dir 解密后的文件存储路径
     * @param toFileName 解密后的文件名称
     * @param secretKey 密钥
     */
    public static File decryptFile(File sourceFile, String dir, String toFileName, String secretKey) {
        File decryptFile = new File(dir, toFileName);
        Cipher cipher = initFileAESCipher(secretKey, Cipher.DECRYPT_MODE);

        try (FileInputStream inputStream = new FileInputStream(sourceFile);
             CipherOutputStream cipherOutputStream = new CipherOutputStream(new FileOutputStream(decryptFile), cipher)) {

            byte[] buffer = new byte[1024 * 2];
            int len;
            while ((len = inputStream.read(buffer)) >= 0) {
                cipherOutputStream.write(buffer, 0, len);
                cipherOutputStream.flush();
            }
            return decryptFile;
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 初始化 AES Cipher
     *
     * @param secretKey 密钥
     * @param cipherMode 加密模式
     * @return 密钥
     */
    private static Cipher initFileAESCipher(String secretKey, int cipherMode) {
        try {
            SecretKeySpec secretKeySpec = getSecretKey(secretKey); // 创建密钥规格
            Cipher cipher = Cipher.getInstance(FILE_CIPHER_ALGORITHM); // 获取密钥
            cipher.init(cipherMode, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
            return cipher;

        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
